/*
 * Copyright (c) 2011, Michael van der Westhuizen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef PYXMLSEC1_NODESET_C_MCVDW_20110216
#define PYXMLSEC1_NODESET_C_MCVDW_20110216

#include "pyxmlsec.h"

#include <xmlsec/nodeset.h>



typedef struct {
    PyObject * callback;
    PyObject * data;
} xmlsec1_xmlSecNodeSetWalkCallbackDataType;


static int xmlsec1_xmlSecNodeSetWalkCallback(xmlSecNodeSetPtr nset, xmlNodePtr cur, xmlNodePtr parent, void * data)
{
    PyGILState_STATE gstate;
    PyObject * nset_;
    PyObject * cur_;
    PyObject * parent_;
    xmlsec1_xmlSecNodeSetWalkCallbackDataType * data_;
    PyObject * dict;
    PyObject * res;
    int ret;

    ret = -1;
    data_ = (xmlsec1_xmlSecNodeSetWalkCallbackDataType *)data;

    gstate = PyGILState_Ensure();

    if (!data_ || !PyCallable_Check(data_->callback))
    {
        PyGILState_Release(gstate);
        return -1;
    }

    if (NULL == (nset_ = xmlsec1mod_wrap_xmlSecNodeSetPtr(nset)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlSecNodeSetPtr);
        PyGILState_Release(gstate);
        return -1;
    }

    if (NULL == (cur_ = xmlsec1mod_wrap_xmlNodePtr(cur)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlNodePtr);
        Py_DECREF(nset_);
        PyGILState_Release(gstate);
        return -1;
    }

    if (NULL == (parent_ = xmlsec1mod_wrap_xmlNodePtr(parent)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlNodePtr);
        Py_DECREF(cur_);
        Py_DECREF(nset_);
        PyGILState_Release(gstate);
        return -1;
    }

    Py_XINCREF(data_->data);

    if (data_->data)
    {
        dict = Py_BuildValue("{s:O, s:O, s:O, s:O}", "nset", nset_, "cur", cur_, "parent", parent_, "data", data_->data);
    }
    else
    {
        dict = Py_BuildValue("{s:O, s:O, s:O}", "nset", nset_, "cur", cur_, "parent", parent_);
    }

    Py_XDECREF(data_->data);
    Py_DECREF(parent_);
    Py_DECREF(cur_);
    Py_DECREF(nset_);

    if (NULL == dict)
    {
        ENSURE_EXCEPTION_SET(Py_BuildValue);
        PyGILState_Release(gstate);
        return -1;
    }

    Py_INCREF(data_->callback);

    if (NULL == (res = PyObject_Call(data_->callback, NULL, dict)))
    {
        Py_DECREF(dict);
        Py_DECREF(data_->callback);
        PyGILState_Release(gstate);
        return -1;
    }

    Py_DECREF(dict);
    Py_DECREF(data_->callback);

    if (Py_None == res)
    {
        ret = 0;
    }
    else
    {
        if (!PyBool_Check(res))
        {
            Py_DECREF(res);
            PyGILState_Release(gstate);
            return -1;
        }

        if (Py_True == res)
        {
            ret = 0;
        }
        else
        {
            ret = -1;
        }
    }

    Py_DECREF(res);
    PyGILState_Release(gstate);
    return ret;
}



static PyObject * xmlsec1_xmlSecNodeSetCreate(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    int arg3;
    xmlDocPtr doc;
    xmlNodeSetPtr nodes;
    xmlSecNodeSetType type;
    xmlSecNodeSetPtr ptr;
    PyObject * ret;

    static char * kwlist[] = {"doc", "nodes", "otype", NULL};

    arg1 = arg2 = NULL;
    arg3 = 0;
    doc = NULL;
    nodes = NULL;
    type = xmlSecNodeSetNormal;
    ptr = NULL;
    ret = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi", kwlist, &arg1, &arg2, &arg3))
    {
        return NULL;
    }

    if (NULL == (doc = xmlsec1mod_unwrap_xmlDocPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlDocPtr);
        return NULL;
    }

    if (NULL == (nodes = xmlsec1mod_SequenceToXmlNodeSetPtr(arg2)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_SequenceToXmlNodeSetPtr);
        return NULL;
    }

    type = (xmlSecNodeSetType)arg3;

    if (NULL == (ptr = xmlSecNodeSetCreate(doc, nodes, type)))
    {
        ENSURE_EXCEPTION_SET(xmlSecNodeSetCreate);
        xmlFree(nodes->nodeTab);
        xmlFree(nodes);
        return NULL;
    }

    if (NULL == (ret = xmlsec1mod_wrap_xmlSecNodeSetPtr(ptr)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlSecNodeSetPtr);
        xmlSecNodeSetDestroy(ptr); /* also destroys the xmlNodeSetPtr (but not the pointed to nodes) */
        return NULL;
    }

    return ret;
}



static PyObject * xmlsec1_xmlSecNodeSetDestroy(PyObject * self, PyObject * arg)
{
    xmlSecNodeSetPtr nset;

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    xmlSecNodeSetDestroy(nset);

    if (PyErr_Occurred())
    {
        return NULL;
    }

    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecNodeSetDocDestroy(PyObject * self, PyObject * arg)
{
    xmlSecNodeSetPtr nset;

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    xmlSecNodeSetDocDestroy(nset);

    if (PyErr_Occurred())
    {
        return NULL;
    }

    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecNodeSetContains(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    PyObject * arg3;
    xmlSecNodeSetPtr nset;
    xmlNodePtr node;
    xmlNodePtr parent;
    
    static char * kwlist[] = {"nset", "node", "parent", NULL};

    arg1 = arg2 = arg3 = NULL;
    nset = NULL;
    node = parent = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO", kwlist, &arg1, &arg2, &arg3))
    {
        return NULL;
    }

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    if (NULL == (node = xmlsec1mod_unwrap_xmlNodePtr(arg2)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlNodePtr);
        return NULL;
    }

    if (NULL == (parent = xmlsec1mod_unwrap_xmlNodePtr(arg3)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlNodePtr);
        return NULL;
    }

    switch (xmlSecNodeSetContains(nset, node, parent))
    {
        case 0:
        {
            Py_RETURN_FALSE;
        }
        case 1:
        {
            Py_RETURN_TRUE;
        }
        default:
        {
            /* de nada */
            break;
        }
    }

    ENSURE_EXCEPTION_SET(xmlSecNodeSetContains);
    return NULL;
}



static PyObject * xmlsec1_xmlSecNodeSetAdd(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    int arg3;
    xmlSecNodeSetPtr nset;
    xmlSecNodeSetPtr newNSet;
    xmlSecNodeSetOp op;
    xmlSecNodeSetPtr ptr;
    PyObject * ret;
   
    static char * kwlist[] = {"nset", "newNSet", "op", NULL};

    arg1 = arg2 = NULL;
    arg3 = 0;
    nset = newNSet = NULL;
    op = xmlSecNodeSetIntersection;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi", kwlist, &arg1, &arg2, &arg3))
    {
        return NULL;
    }

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    if (NULL == (newNSet = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg2)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    op = (xmlSecNodeSetOp)arg3;

    if (NULL == (ptr = xmlSecNodeSetAdd(nset, newNSet, op)))
    {
        ENSURE_EXCEPTION_SET(xmlSecNodeSetAdd);
        return NULL;
    }

    if (NULL == (ret = xmlsec1mod_wrap_xmlSecNodeSetPtr(ptr)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlSecNodeSetPtr);
        xmlSecNodeSetDestroy(ptr);
        return NULL;
    }

    return ret;
}



static PyObject * xmlsec1_xmlSecNodeSetAddList(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    int arg3;
    xmlSecNodeSetPtr nset;
    xmlSecNodeSetPtr newNSet;
    xmlSecNodeSetOp op;
    xmlSecNodeSetPtr ptr;
    PyObject * ret;

    static char * kwlist[] = {"nset", "newNSet", "op", NULL};

    arg1 = arg2 = NULL;
    arg3 = 0;
    nset = newNSet = NULL;
    op = xmlSecNodeSetIntersection;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi", kwlist, &arg1, &arg2, &arg3))
    {
        return NULL;
    }

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    if (NULL == (newNSet = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg2)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    op = (xmlSecNodeSetOp)arg3;

    if (NULL == (ptr = xmlSecNodeSetAddList(nset, newNSet, op)))
    {
        ENSURE_EXCEPTION_SET(xmlSecNodeSetAddList);
        return NULL;
    }

    if (NULL == (ret = xmlsec1mod_wrap_xmlSecNodeSetPtr(ptr)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlSecNodeSetPtr);
        xmlSecNodeSetDestroy(ptr);
        return NULL;
    }

    return ret;
}



static PyObject * xmlsec1_xmlSecNodeSetGetChildren(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    int withComments;
    int invert;
    xmlDocPtr doc;
    xmlNodePtr parent;
    xmlSecNodeSetPtr ptr;
    PyObject * ret;

    static char * kwlist[] = {"doc", "parent", "withComments", "invert", NULL};

    arg1 = arg2 = NULL;
    withComments = invert = 0;
    doc = NULL;
    parent = NULL;
    ptr = NULL;
    ret = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|ii", kwlist, &arg1, &arg2, &withComments, &invert))
    {
        return NULL;
    }

    if (NULL == (doc = xmlsec1mod_unwrap_xmlDocPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlDocPtr);
        return NULL;
    }

    if (NULL == (parent = xmlsec1mod_unwrap_xmlNodePtr(arg2)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlNodePtr);
        return NULL;
    }

    if (NULL == (ptr = xmlSecNodeSetGetChildren(doc, parent, withComments, invert)))
    {
        ENSURE_EXCEPTION_SET(xmlSecNodeSetGetChildren);
        return NULL;
    }

    if (NULL == (ret = xmlsec1mod_wrap_xmlSecNodeSetPtr(ptr)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlSecNodeSetPtr);
        xmlSecNodeSetDestroy(ptr);
        return NULL;
    }

    return ret;
}



static PyObject * xmlsec1_xmlSecNodeSetWalk(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    PyObject * arg3;
    xmlSecNodeSetPtr nset;
    xmlsec1_xmlSecNodeSetWalkCallbackDataType userdata;

    static char * kwlist[] = {"doc", "walkFunc", "data", NULL};

    arg1 = arg2 = arg3 = NULL;
    nset = NULL;
    memset(&userdata, '\0', sizeof(userdata));

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, &arg1, &arg2, &arg3))
    {
        return NULL;
    }

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    if (!PyCallable_Check(arg2))
    {
        PyErr_SetString(PyExc_TypeError, "walkFunc must be callable");
        return NULL;
    }

    userdata.callback = arg2;
    userdata.data = arg3;

    if (0 != xmlSecNodeSetWalk(nset, &xmlsec1_xmlSecNodeSetWalkCallback, &userdata))
    {
        ENSURE_EXCEPTION_SET(xmlSecNodeSetWalk);
        return NULL;
    }

    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecNodeSetDumpTextNodes(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    xmlSecNodeSetPtr nset;
    xmlOutputBufferPtr out;

    static char * kwlist[] = {"nset", "out", NULL};

    arg1 = arg2 = NULL;
    nset = NULL;
    out = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &arg1, &arg2))
    {
        return NULL;
    }

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    if (NULL == (out = xmlsec1mod_unwrap_xmlOutputBufferPtr(arg2)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlOutputBufferPtr);
        return NULL;
    }

    if (0 != xmlSecNodeSetDumpTextNodes(nset, out))
    {
        ENSURE_EXCEPTION_SET(xmlSecNodeSetDumpTextNodes);
        return NULL;
    }

    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecNodeSetDebugDump(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * arg1;
    PyObject * arg2;
    xmlSecNodeSetPtr nset;
    PyFileObject * output;
    FILE * fp;

    static char * kwlist[] = {"nset", "output", NULL};

    arg1 = arg2 = NULL;
    nset = NULL;
    fp = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &arg1, &arg2))
    {
        return NULL;
    }

    if (NULL == (nset = xmlsec1mod_unwrap_xmlSecNodeSetPtr(arg1)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_unwrap_xmlSecNodeSetPtr);
        return NULL;
    }

    if (!PyFile_Check(arg2))
    {
        PyErr_SetString(PyExc_TypeError, "output must be a file opened for writing");
    }

    output = (PyFileObject *)arg2;

    if (NULL == (fp = PyFile_AsFile(arg2)))
    {
        ENSURE_EXCEPTION_SET(PyFile_AsFile);
        return NULL;
    }

    Py_INCREF(arg1);
    PyFile_IncUseCount(output);
    Py_BEGIN_ALLOW_THREADS
    xmlSecNodeSetDebugDump(nset, fp);
    Py_END_ALLOW_THREADS
    PyFile_DecUseCount(output);
    Py_DECREF(arg1);

    Py_RETURN_NONE;
}

#endif
